package com.alibaba.chaosblade.box.controller.application;

import com.alibaba.chaosblade.box.annotation.LoginUser;
import com.alibaba.chaosblade.box.common.app.sdk.scope.Host;
import com.alibaba.chaosblade.box.common.common.domain.ChaosError;
import com.alibaba.chaosblade.box.common.common.domain.Response;
import com.alibaba.chaosblade.box.common.common.domain.task.BaseExperimentTask;
import com.alibaba.chaosblade.box.common.common.domain.user.ChaosUser;
import com.alibaba.chaosblade.box.common.common.enums.CommonErrorCode;
import com.alibaba.chaosblade.box.common.experiment.model.AppNameAndIdPair;
import com.alibaba.chaosblade.box.common.infrastructure.constant.PermissionTypes;
import com.alibaba.chaosblade.box.common.infrastructure.util.CollectionUtil;
import com.alibaba.chaosblade.box.controller.BaseController;
import com.alibaba.chaosblade.box.dao.model.base.PageableResponse;
import com.alibaba.chaosblade.box.model.RestResponseUtil;
import com.alibaba.chaosblade.box.service.ApplicationService;
import com.alibaba.chaosblade.box.service.auth.perimission.UserPermissionService;
import com.alibaba.chaosblade.box.service.infrastructure.configuration.ApplicationConfiguration;
import com.alibaba.chaosblade.box.service.model.RestResponse;
import com.alibaba.chaosblade.box.service.model.application.*;
import com.alibaba.chaosblade.box.service.model.device.CloudDevice;
import com.alibaba.chaosblade.box.service.model.experiment.ExperimentScope;
import com.google.common.base.Preconditions;
import com.google.common.base.Strings;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.Set;

/**
 * @author haibin
 */
@RestController
public class ApplicationController extends BaseController {

    @Autowired
    private ApplicationService applicationService;

    @Autowired
    private UserPermissionService userPermissionService;

    /**
     * 获取用户的应用信息概况，包括了基本信息以及演练情况
     *
     * @param user                               当前用户
     * @param getUserApplicationSummariesRequest 查询请求，支持分页
     * @return 用户应用信息情况
     */
    @PostMapping(value = "GetUserApplicationSummaries")
    public RestResponse<PageableResponse<UserApplicationSummary>> getUserApplicationSummaries(@LoginUser ChaosUser user,
                                                                                              @RequestBody GetUserApplicationSummariesRequest getUserApplicationSummariesRequest) {
        getUserApplicationSummariesRequest.setUser(user);
        return RestResponseUtil.initWithServiceResponse(
                applicationService.getUserApplicationSummaries(getUserApplicationSummariesRequest));
    }

    /**
     * 搜索应用
     *
     * @param user                               当前登录用户
     * @param userApplicationSearchRequest       搜索请求参数，包含搜索关键字等
     * @return                                   应用搜索结果列表
     */
    @PostMapping(value = "SearchApplications")
    public RestResponse<List<UserApplicationSummary>> searchApplications(@LoginUser ChaosUser user,
                                                                         @RequestBody UserApplicationSearchRequest userApplicationSearchRequest) {
        userApplicationSearchRequest.setUser(user);
        return RestResponseUtil.initWithServiceResponse(
                applicationService.searchApplicationsForSummary(userApplicationSearchRequest));
    }

    /**
     * 获取应用基本信息
     *
     * @param user                               当前登录用户
     * @param userApplicationQueryRequest        查询请求参数，包含应用ID或名称等
     * @return                                   应用基本信息
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetApplicationBasic")
    public RestResponse<ApplicationBasicInfo> getApplicationBasic(@LoginUser ChaosUser user,
                                                                  @RequestBody UserApplicationQueryRequest userApplicationQueryRequest) throws Throwable {

        userApplicationQueryRequest.setUser(user);
        if (userApplicationQueryRequest.getAppId() != null) {
            userPermissionService.checkUserApplicationPermission(PermissionTypes.R, user,
                    userApplicationQueryRequest.getAppId());
        } else {
            userPermissionService.checkUserApplicationPermission(PermissionTypes.R, user,
                    userApplicationQueryRequest.getNamespace(), userApplicationQueryRequest.getAppName());
        }
        return RestResponseUtil.initWithServiceResponse(
                applicationService.getApplicationBasic(userApplicationQueryRequest));
    }

    /**
     * 停止指定应用下的所有演练任务
     *
     * @param chaosUser                          当前登录用户
     * @param userApplicationQueryRequest        查询请求参数，包含应用命名空间和名称
     * @return                                   停止的演练任务数量
     */
    @PostMapping(value = "/StopExperimentsByApplication")
    public RestResponse<Integer> stopExperimentsByApplication(@LoginUser ChaosUser chaosUser,
                                                              @RequestBody UserApplicationQueryRequest userApplicationQueryRequest) {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.W,
                chaosUser, userApplicationQueryRequest.getNamespace(),
                userApplicationQueryRequest.getAppName());
        return RestResponseUtil.initWithServiceResponse(
                applicationService.stopExperimentsByApplication(userApplicationQueryRequest));
    }

    /**
     * 获取包含当前应用信息的演练任务记录
     *
     * @param user                               当前登录用户
     * @param userApplicationQueryRequest        查询请求参数，包含应用ID
     * @return                                   分页的演练任务记录
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetApplicationExperimentTasks")
    public RestResponse<PageableResponse<BaseExperimentTask>> getApplicationExperimentTasks(@LoginUser ChaosUser user,
                                                                                            @RequestBody UserApplicationQueryRequest userApplicationQueryRequest) throws Throwable {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.R,
                user,
                userApplicationQueryRequest.getAppId());
        return RestResponseUtil.initWithServiceResponse(
                applicationService.getApplicationExperimentTasks(userApplicationQueryRequest));
    }

    /**
     * 根据应用ID获取应用下的机器列表
     *
     * @param user                               当前登录用户
     * @param applicationScopeSearchRequest      查询请求参数，包含应用ID、分组名等
     * @return                                   分页的机器列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetApplicationHosts")
    public RestResponse<PageableResponse<ExperimentScope>> getApplicationHosts(@LoginUser ChaosUser user,
                                                                               @RequestBody ApplicationScopeSearchRequest applicationScopeSearchRequest) throws Throwable {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.R,
                user,
                applicationScopeSearchRequest.getAppId());
        return RestResponseUtil.initWithServiceResponse(
                applicationService.searchApplicationHosts(applicationScopeSearchRequest));
    }

    /**
     * 搜索应用下的机器列表
     *
     * @param user                               当前登录用户
     * @param applicationScopeSearchRequest      查询请求参数，包含搜索关键字等
     * @return                                   分页的机器列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "SearchApplicationHosts")
    public RestResponse<PageableResponse<ExperimentScope>> searchApplicationHosts(@LoginUser ChaosUser user,
                                                                                  @RequestBody ApplicationScopeSearchRequest applicationScopeSearchRequest) throws Throwable {
        return RestResponseUtil.initWithServiceResponse(
                applicationService.searchApplicationHosts(applicationScopeSearchRequest));
    }

    /**
     * 处理获取用户应用列表的请求
     * 当请求的大小为10时，自动调整为50，以优化查询效率
     * 此方法首先根据条件查询应用列表，然后根据用户权限过滤应用列表
     *
     * @param user                     登录用户信息，用于权限验证和个性化设置
     * @param applicationSearchRequest 包含应用查询条件的请求体
     * @return 返回包含应用名称和ID的分页响应
     * @throws Throwable 如果服务处理过程中发生错误，抛出异常
     *                   java.sql.SQLSyntaxErrorException: Expression #1 of SELECT list is not in GROUP BY clause and contains nonaggregated column 'chaosblade.t_chaos_application_device.id' which is not functionally dependent on columns in GROUP BY clause; this is incompatible with sql_mode=only_full_group_by
     */
    @PostMapping(value = "GetUserApplications")
    public RestResponse<PageableResponse<AppNameAndIdPair>> getUserApplications(@LoginUser ChaosUser user,
                                                                                @RequestBody ApplicationSearchRequest applicationSearchRequest) throws Throwable {
        if (applicationSearchRequest.getSize() == 10) {
            applicationSearchRequest.setSize(50);
        }
        applicationSearchRequest.setUser(user);
        Response<PageableResponse<AppNameAndIdPair>> listResponse = applicationService.pageUserApplicationNames(applicationSearchRequest);
        userPermissionService.filterApplicationPair(user, listResponse);
        return RestResponseUtil.initWithServiceResponse(listResponse);
    }

    /**
     * 获取指定应用的分组列表
     *
     * @param user                               当前登录用户
     * @param userApplicationQueryRequest        查询请求参数，包含应用ID
     * @return                                   应用分组列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetUserApplicationGroups")
    public RestResponse<List<String>> getUserApplicationGroups(@LoginUser ChaosUser user,
                                                               @RequestBody UserApplicationQueryRequest userApplicationQueryRequest) throws Throwable {

        try {
            userPermissionService.checkUserApplicationPermission(PermissionTypes.R, user, userApplicationQueryRequest.getAppId());
        } catch (IllegalArgumentException e) {
            return RestResponseUtil.failed(new ChaosError(CommonErrorCode.P_APPLICATION_ID_REQUIRED));
        }
        return RestResponseUtil.initWithServiceResponse(
                applicationService.getGroupsByApp(userApplicationQueryRequest));
    }

    /**
     * 根据应用ID获取应用下的机器列表（用于演练创建时选择机器）
     *
     * @param user                               当前登录用户
     * @param pageableQueryApplicationHostRequest 查询请求参数，包含应用ID等
     * @return                                   分页的机器列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetScopesByApplication")
    public RestResponse<PageableResponse<CloudDevice>> getScopesByApplication(@LoginUser ChaosUser user,
                                                                              @RequestBody PageableQueryApplicationHostRequest pageableQueryApplicationHostRequest) throws Throwable {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.R,
                user,
                pageableQueryApplicationHostRequest.getAppId());
        return RestResponseUtil.initWithServiceResponse(applicationService.getScopesByApplication(user, pageableQueryApplicationHostRequest));
    }

    /**
     * 列举应用的配置列表
     *
     * @param user                               当前登录用户
     * @param userApplicationQueryRequest        查询请求参数，包含应用ID
     * @return                                   应用配置列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "ListApplicationConfigurations")
    public RestResponse<List<ApplicationConfiguration>> listApplicationConfigurations(@LoginUser ChaosUser user,
                                                                                      @RequestBody UserApplicationQueryRequest userApplicationQueryRequest) throws Throwable {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.R, user, userApplicationQueryRequest.getAppId());

        return RestResponseUtil.initWithServiceResponse(applicationService.listApplicationConfigurations(userApplicationQueryRequest));
    }

    /**
     * 更新应用配置
     *
     * @param user                               当前登录用户
     * @param configurationUpdateRequest         更新请求参数，包含配置信息
     * @return                                   更新结果
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "UpdateApplicationConfiguration")
    public RestResponse updateApplicationConfiguration(@LoginUser ChaosUser user,
                                                       @RequestBody ApplicationConfigurationUpdateRequest configurationUpdateRequest) throws Throwable {
        userPermissionService.checkUserApplicationPermission(PermissionTypes.R,
                user,
                Long.valueOf(configurationUpdateRequest.getAppId()));
        return RestResponseUtil.initWithServiceResponse(
                applicationService.updateApplicationConfiguration(configurationUpdateRequest));
    }

    /**
     * 更新应用下机器标签
     *
     * @param user                               当前登录用户
     * @param applicationTagsUpdateRequest       更新请求参数，包含标签信息
     * @return                                   更新结果
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "UpdateApplicationTag")
    public RestResponse<Boolean> updateApplicationTag(@LoginUser ChaosUser user,
                                                      @RequestBody ApplicationTagsUpdateRequest applicationTagsUpdateRequest) throws Throwable {
        applicationTagsUpdateRequest.setUser(user);
        userPermissionService.checkUserApplicationPermission(PermissionTypes.W, user, Long.valueOf(applicationTagsUpdateRequest.getAppId()));
        return RestResponseUtil.initWithServiceResponse(applicationService.updateApplicationTag(applicationTagsUpdateRequest));
    }

    /**
     * 批量添加标签（仅添加）
     *
     * @param user                               当前登录用户
     * @param applicationTagsBatchUpdateRequest  批量更新请求参数，包含标签信息
     * @return                                   更新结果
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "BatchAddApplicationTag")
    public RestResponse<Boolean> batchAddApplicationTag(@LoginUser ChaosUser user,
                                                        @RequestBody ApplicationTagsBatchUpdateRequest applicationTagsBatchUpdateRequest) throws Throwable {
        applicationTagsBatchUpdateRequest.setUser(user);
        userPermissionService.checkUserApplicationPermission(PermissionTypes.W,
                user,
                applicationTagsBatchUpdateRequest.getAppId());
        return RestResponseUtil.initWithServiceResponse(
                applicationService.batchAddApplicationTag(applicationTagsBatchUpdateRequest));
    }

    /**
     * 模糊搜索查询分组下所有标签
     *
     * @param user                               当前登录用户
     * @param applicationTagsUpdateRequest       搜索请求参数，包含搜索关键字等
     * @return                                   标签列表
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "SearchDeviceTags")
    public RestResponse<List<String>> searchDeviceTags(@LoginUser ChaosUser user,
                                                       @RequestBody ApplicationTagsSearchRequest applicationTagsUpdateRequest) throws Throwable {
        applicationTagsUpdateRequest.setUser(user);
        return RestResponseUtil.initWithServiceResponse(
                applicationService.searchDeviceTagsByAppIdAndGroupName(applicationTagsUpdateRequest));
    }

    /**
     * 模糊搜索查询分组下所有机器的namespace
     *
     * @param user                               当前登录用户
     * @param applicationTagsUpdateRequest       搜索请求参数，包含应用ID和分组名
     * @return                                   namespace集合
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "SearchClusterNamespace")
    public RestResponse<Set<String>> searchClusterNamespace(@LoginUser ChaosUser user,
                                                            @RequestBody ApplicationTagsSearchRequest applicationTagsUpdateRequest) throws Throwable {
        Preconditions.checkArgument(applicationTagsUpdateRequest.getAppId() != null, "appId id is required");
        Preconditions.checkArgument(!CollectionUtil.isNullOrEmpty(applicationTagsUpdateRequest.getGroupNames()), "group is required");
        applicationTagsUpdateRequest.setUser(user);
        return RestResponseUtil.initWithServiceResponse(
                applicationService.searchClusterNamespacesByAppIdAndGroupName(applicationTagsUpdateRequest));
    }

    /**
     * 获取当前应用分组下存活机器数量
     *
     * @param user                               当前登录用户
     * @param applicationHostsCountRequest       查询请求参数，包含应用ID和分组名
     * @return                                   机器数量统计信息
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "CountScopesByApplication")
    public RestResponse<ApplicationHostsCount> countScopesByApplication(@LoginUser ChaosUser user,
                                                                        @RequestBody ApplicationHostsCountRequest applicationHostsCountRequest) throws Throwable {
        return RestResponseUtil.initWithServiceResponse(
                applicationService.countScopesByApplication(user, applicationHostsCountRequest));
    }

    /**
     * 根据app configurationId获取机器信息
     *
     * @param user                               当前登录用户
     * @param applicationScopeQueryRequest       查询请求参数，包含配置ID
     * @return                                   机器信息
     * @throws Throwable                         如果服务处理过程中发生错误，抛出异常
     */
    @PostMapping(value = "GetScopeByAppConfigurationId")
    public RestResponse<Host> getApplicationScopeByConfigurationId(@LoginUser ChaosUser user,
                                                                   @RequestBody ApplicationScopeQueryRequest applicationScopeQueryRequest) throws Throwable {
        if (Strings.isNullOrEmpty(applicationScopeQueryRequest.getAppConfigurationId())) {
            return RestResponseUtil.okWithData(null);
        }
        return RestResponseUtil.initWithServiceResponse(applicationService.getScopeByAppConfigurationId(applicationScopeQueryRequest.getAppConfigurationId()));
    }
}
